// 这段代码定义了一个名为 LockMethod 的类，它使用一个 Lock 对象来实现对代码块的加锁和解锁。
package liming.tool.lock;


import liming.tool.runnable.HaveReturnValueInterface;
import liming.tool.runnable.NoReturnValueInterface;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;


public class CodeLock{
    protected ReentrantLock lock;

    public CodeLock() {
        this(new ReentrantLock());
    }

    public CodeLock(ReentrantLock lock) {
        if (lock == null) {
            throw new NullPointerException("Lock cannot be null.");
        }
        this.lock = lock;
    }

    /**
     * 执行一个带锁的任务，保持
     * @param action 无返回值任务
     */
    public void lock(NoReturnValueInterface action) {
        if (action == null) return;
        boolean getLock = false;
        try {
            lock.lockInterruptibly();
            getLock = true;
            action.run();
        } catch (Exception e) {
            action.onException(e);
        } finally {
            if (getLock) lock.unlock();
        }
    }

    public void lock(NoReturnValueInterface action, long time, TimeUnit unit) {
        if (action == null) return;
        boolean getLock = false;
        try {
            getLock = lock.tryLock(time, unit);
            if (getLock) action.run();
            else action.onTimeout();
        } catch (Exception e) {
            action.onException(e);
        } finally {
            if (getLock) lock.unlock();
        }
    }

    public <T> T lock(HaveReturnValueInterface<T> operation) {
        if (operation == null)
            return null;
        boolean getLock = false;
        try {
            lock.lockInterruptibly();
            getLock = true;
            return operation.run();
        } catch (Exception e) {
            return operation.onException(e);
        } finally {
            if (getLock) lock.unlock();
        }
    }

    public <T> T lock(HaveReturnValueInterface<T> operation, long time, TimeUnit unit) {
        if (operation == null) return null;
        boolean getLock = false;
        try {
            getLock = lock.tryLock(time, unit);
            if (getLock) return operation.run();
            else return operation.onTimeout();
        } catch (Exception e) {
            return operation.onException(e);
        } finally {
            if (getLock) lock.unlock();
        }
    }

    /**
     * 在无锁状态下执行无返回值的任务。
     * 如果当前线程持有锁，则在任务执行前释放所有锁，任务完成后重新获取。
     * @param action 要执行的任务
     */
    public void unlock(NoReturnValueInterface action) {
        if (action == null) return;
        boolean wasLocked = lock.isHeldByCurrentThread();
        int holdCount = 0;
        if (wasLocked) {
            holdCount = lock.getHoldCount();
            for (int i = 0; i < holdCount; i++) {
                lock.unlock();
            }
        }
        try {
            action.run();
        } catch (Exception e) {
            action.onException(e);
        } finally {
            if (wasLocked) {
                for (int i = 0; i < holdCount; i++) {
                    try {
                        lock.lock();
                    } catch (Exception e) {
                        Thread.currentThread().interrupt();
                        throw new RuntimeException("Failed to reacquire lock", e);
                    }
                }
            }
        }
    }

    /**
     * 在无锁状态下执行有返回值的任务。
     * 如果当前线程持有锁，则在任务执行前释放所有锁，任务完成后重新获取。
     * @param operation 要执行的操作
     * @return 操作返回的结果
     */
    public <T> T unlock(HaveReturnValueInterface<T> operation) {
        if (operation == null) return null;
        boolean wasLocked = lock.isHeldByCurrentThread();
        int holdCount = 0;
        if (wasLocked) {
            holdCount = lock.getHoldCount();
            for (int i = 0; i < holdCount; i++) {
                lock.unlock();
            }
        }
        try {
            return operation.run();
        } catch (Exception e) {
            return operation.onException(e);
        } finally {
            if (wasLocked) {
                for (int i = 0; i < holdCount; i++) {
                    try {
                        lock.lock();
                    } catch (Exception e) {
                        Thread.currentThread().interrupt();
                        throw new RuntimeException("Failed to reacquire lock", e);
                    }
                }
            }
        }
    }

    public CodeCondition newCondition() {
        return new CodeCondition(this, lock.newCondition());
    }

    @Override
    public final String toString() {
        return lock.toString()+" "+lock.getHoldCount();
    }
}